Skip to content

fix(core,runtime,spec): the in-memory i18n fallback learns the declared i18n.fallbackLocale - #16088

Merged
os-zhuang merged 5 commits into
mainfrom
claude/issue-15694-memory-i18n-fallback-locale
Sep 6, 2026
Merged

fix(core,runtime,spec): the in-memory i18n fallback learns the declared i18n.fallbackLocale#16088
os-zhuang merged 5 commits into
mainfrom
claude/issue-15694-memory-i18n-fallback-locale

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #15694

What was wrong

i18n.fallbackLocale is authorable on the stack artifact (TranslationConfigSchema), and FileI18nAdapter — the provider I18nServicePlugin installs — has always honoured it. Both boot paths construct it with the same collapse, fallbackLocale || defaultLocale || 'en': packages/cli/src/commands/serve.ts:3125 and plugin-dev's dev-i18n.ts:247, reached from dev-plugin.ts:575. Its t() consults that locale, per key, after the requested one (file-i18n-adapter.ts:147).

The kernel's in-memory fallback is constructed with nothing. AppPlugin.loadTranslations injected the declared defaultLocale and supportedLocales (#7679) into whichever i18n service was registered, but never fallbackLocale, and the provider had no setter to receive one. On every stack running that fallback — any stack that declares translations without @objectstack/service-i18n registered (not installed, or tierEnabled('i18n') false) — the declaration was inert.

Reproduced against this branch's original base c2520416c, with a live positive control on every zero reading:

reading result live control on the same file and pathspec
fallbackLocale in packages/core/src/fallbacks/memory-i18n.ts 0 hits (grep exit 1) mergedLocale(defaultLocale) hits at line 147
setFallbackLocale in packages/runtime/src/app-plugin.ts 0 hits (grep exit 1) the two existing setters: 7 hits

The comment filter matters on the first one and was run: case-insensitively, fallback hits that file 9 times — it lives in src/fallbacks/ and is named for being the fallback provider. The identifier fallbackLocale was still 0.

One stack (defaultLocale: 'zh-CN', fallbackLocale: 'en', key present only in en), same call t('objects.property.tip', 'zh-CN'):

provider answer
memory fallback, pre-fix "objects.property.tip" — the key, i.e. no fallback at all
FileI18nAdapter (the contrast surface, untouched) "Only in English"
memory fallback, post-fix "Only in English"

The control is what makes this a divergence rather than a meaningless declaration. That the memory fallback self-declares degraded licenses fewer capabilities, not a different answer to the same declared key.

What changed

  • II18nService.setFallbackLocale?(locale) (packages/spec/src/contracts/i18n-service.ts) — a new optional member, the injection counterpart of the existing getFallbackLocale?(), same shape as setDefaultLocale? and setSupportedLocales?.
  • createMemoryI18n receives it and acts on it (packages/core/src/fallbacks/memory-i18n.ts) — t() consults the declared fallback per key after the requested locale, the same second leg FileI18nAdapter.t() has. Per key, not per bundle: the pre-existing resolveTranslations(locale) ?? mergedLocale(defaultLocale) line swaps whole bundles and only when the requested locale has none, so a zh-CN bundle that simply lacked the key never reached anything else. That older leg is unchanged and pinned.
  • AppPlugin.loadTranslations threads the declaration (packages/runtime/src/app-plugin.ts) — through the same typeof ... === 'function' optional-capability probe as setDefaultLocale, guarded on the app having declared something (several AppPlugins can share one kernel, and an app that declares no i18n block must not clear a fallback another app declared). Injection order is now setDefaultLocale, setFallbackLocale, setSupportedLocales, loadTranslations.

A stack that declares no fallbackLocale gets exactly the behaviour it has today — pinned in both suites.

Deliberately NOT done

getFallbackLocale() is not added to the memory fallback. The setter is what the provider is told; the accessor is what the serving layer asks it when building the metadata-document translators' fallback chain (#14882). Answering the second from defaultLocale — the only value always available there — would settle the default-locale contract question #14882 leaves deliberately open, from a degraded provider. Those reads keep the resolvers' own default, which is known and intentional. The fence is pinned as a test and written into the provider's docblock.

packages/services/service-i18n/src/file-i18n-adapter.ts is untouched — it is the contrast surface, not a defect.

Clause-2 — this widens a published surface, on two carriers

Measured with the corrected instrument: every declaration file each changed package publishes (all .d.ts / .d.mts / .d.cts under dist, which files[] ships whole and the exports map points into), not the root barrel. Built at head, the three sources swapped back to c2520416c, rebuilt, snapshotted, restored, rebuilt, diffed.

carrier published declaration files moved
@objectstack/spec 128 yesdist/contracts/index.d.ts and .d.mts gain setFallbackLocale?(locale: string): void; on II18nService
@objectstack/core 4 yesdist/index.d.ts and .d.cts gain setFallbackLocale(locale: string): void; on createMemoryI18n's inferred return type
@objectstack/runtime 2 no — byte-identical both ways

Limb 1: fires. A member is added to a declared, published interface, and to a published factory's return type. Limb 2: does not fire. Nothing is newly accepted or rejected at runtime for any existing caller: the member is optional, the injection is guarded on the app having declared a value, and a stack that declares none is measured identical pre- and post-fix. ⇒ Clause-2 answer: yes, additive-only. needs:contract-review carried on the issue and the PR; PR stays draft.

Two instrument findings worth recording:

  1. The root barrel would have returned a confident false negative. packages/spec/dist/index.d.ts is byte-identical before and after (7f3f9bfe77c57a218e4fa93abd6ed57c9b0ab274 in both snapshots) and contains 0 occurrences of II18nService — live control on the same file: 100 export lines. The contract is published only through the ./contracts subpath, which carries 5 occurrences. A re-export barrel cannot change for a subpath edit.
  2. packages/spec/dist/ui/index.d.ts differs but is not a surface move. 154 added and 154 removed lines; after normalising union member order the two line sets are identical, and the file contains no setFallbackLocale. Adding an interface member permutes TypeScript's union member emission elsewhere. Excluded from the verdict as measured noise, not assumed to be.

packages/spec/api-surface/** (17 files) is byte-identical before and after, and check:api-surface reports the surface unchanged. Both are correct and neither contradicts the above: that ledger records top-level export names, not interface members, which is why the answer comes from the declaration diff instead.

Was the interface member necessary, or would the capability probe alone have sufficed?

Asked explicitly, so measured explicitly. The member is necessary. Instrument: revert only the spec interface, keep the app-plugin.ts call site, rebuild @objectstack/spec (preflight confirms the marker is absent from all 217 built files), then tsc --noEmit on @objectstack/runtime:

src/app-plugin.ts(1826,62): error TS2551: Property 'setFallbackLocale' does not exist on type 'II18nService'. Did you mean 'getFallbackLocale'?
src/app-plugin.ts(1827,25): error TS2551: Property 'setFallbackLocale' does not exist on type 'II18nService'. Did you mean 'getFallbackLocale'?

Exactly 2 diagnostics, both at the probe. i18nService is statically typed II18nService | undefined (app-plugin.ts:1745), so the typeof ... === 'function' probe compiles only because the member is declared. The smaller diff exists but costs an as any cast at the call site, which neither setDefaultLocale? nor setSupportedLocales? takes. Declaring the optional member is the shape this file already uses.

Documentation

Nine content/docs pages name a symbol this diff touches. Silence is not falsehood, so only one was repaired.

Repaired — one page, one falsified claim. content/docs/kernel/services-checklist.mdx is the only page that ENUMERATES what AppPlugin.loadTranslations injects, and an enumeration presented as complete is a claim. It read:

AppPlugin will:

  1. Set the default locale via i18nService.setDefaultLocale()
  2. Call i18nService.loadTranslations(locale, data) for each locale in every bundle
  3. Skip gracefully if no i18n service is registered

That list was already one item behind before this branch (it never named setSupportedLocales, #7679). It now names all three setters in code order, states the property they share, and states what FileI18nAdapter actually implements.

Not falsified — left alone, with the sentence checked.

  • content/docs/protocol/kernel/i18n-standard.mdx — "The 'system default' step is the stack's declared fallback (i18n.fallbackLocale, else defaultLocale), never a literal en", and "Missing keys fall back to the fallbackLocale, then return the key string". Contract-level statements, attributed to no single provider. This diff makes them true of both providers; before it they were true of one.
  • content/docs/protocol/kernel/index.mdx — "falls back to the configured fallback locale, returning the key itself when nothing matches". Same: made true, not falsified.
  • content/docs/ui/translations.mdx — states the negotiation chain by its INPUTS (fallbackLocale, else defaultLocale) and shares no identifier with the emitter, so no drift tool can list it. Read by hand for exactly that reason. Made true, not falsified.
  • content/docs/automation/email-templates.mdx — names II18nService.getDefaultLocale() only. Silent on the fallback locale.
  • content/docs/references/system/translation.mdx, content/docs/references/automation/io-node-config.mdx, content/docs/deployment/environment-variables.mdx — the fallbackLocale mentions are a schema table row, a per-recipient locale rule, and a pinyin-default derivation. None touched.

Release-owned, read only. content/docs/releases/v17.mdx names II18nService.getDefaultLocale() at line 3603 (per-recipient notification locale) and has no claim about fallbackLocale, createMemoryI18n or the in-memory i18n fallback anywhere — grep for those returns only an unrelated query-engine "in-memory fallback" at line 1922. Not falsified, nothing owed, not edited.

Verification

Head 22112c954, working tree clean. All heavy work through scripts/pm/os-verify-lock.sh; every exit code captured by redirecting before any pipe; every verdict quoted from the tool's own output.

  • Pin ablation — one shell, trap on EXIT INT TERM, absolute paths, restore via git checkout HEAD -- path never the bare form. The three implementation files swapped back to c2520416c; mutation proven on disk by anchored grep -c in both directions plus git hash-object differing from the HEAD blob on all three; rebuilt, and the mutation proven to have reached the artifact by scripts/ablation-dist-preflight.mjs PACKAGE setFallbackLocale --absent on all three packages; measured; restored; restore proven by a whole-tree git status --porcelain returning 0 lines and blob equality with HEAD; rebuilt; preflight re-run without --absent; re-measured.
    • red: @objectstack/core Tests 10 failed | 51 passed (61); @objectstack/runtime Tests 2 failed | 4 passed (6)
    • green: @objectstack/core Tests 61 passed (61); @objectstack/runtime Tests 6 passed (6)
    • The rebuild leg is belt-and-braces here rather than strictly owed — packages/runtime/vitest.config.ts aliases @objectstack/core and @objectstack/spec to source — but a forgotten ablation rebuild fails GREEN and silently, so it was done and proven anyway.
  • Suites at head: @objectstack/core 50 files / 1215 passed; @objectstack/runtime 233 files / 3311 passed; @objectstack/spec 481 files / 12886 passed.
  • Typecheck: @objectstack/spec + @objectstack/core + @objectstack/runtime, exit 0, including each package's check:test-typecheck against its tsconfig.test.json. Both new/edited test files confirmed inside their tsc program via tsc --listFiles (1 hit each; control on a non-existent path: 0), so that green verdict really does cover them.
  • Gate family re-derived after the final commit: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack — provenance line names objectstack-ai/objectstack at commit 22112c954, --repo ... holds, 7 paths vs merge base b3820c3e2, three-dot. The derived family is byte-identical to the derivation taken before the last commit. 65 gate commands run, 64 exit 0.
    • The one exception is pnpm check:dual-build-cjs-loads, exit 3 with its own verdict PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ ... This is NOT a pass: nothing was measured. It wants a full-repo pnpm build (32 packages have no dist/ in this worktree). Recorded as NOT MEASURED — not a pass, not a failure; CI's Build Core job owns it. Its sibling check:type-check-debt --re-measure carries the same full-workspace-build prerequisite and is likewise left to CI.
  • Repo-wide lint run in full rather than narrowed: pnpm lint = eslint . --no-inline-config, exit 0.
  • pnpm check:nul-bytes exit 0, plus a direct grep -naP control-byte scan over the changed files: no hits.

The base was refreshed by merging origin/main at b3820c3e2 (never a rebase, never a force-push); the Corepack CI fix f50c394da was already an ancestor.

Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ

…ed `i18n.fallbackLocale`

`i18n.fallbackLocale` is authorable and `FileI18nAdapter` honours it, but the
kernel's in-memory fallback was never told it: `AppPlugin.loadTranslations`
injected `defaultLocale` and `supportedLocales` and nothing else, and the
provider had no setter. A stack declaring `defaultLocale: 'zh-CN'` with
`fallbackLocale: 'en'` answered a missing `zh-CN` key from `en` under
`I18nServicePlugin` and from `zh-CN` -- not at all -- under the fallback.

- `II18nService.setFallbackLocale?(locale)`: new optional member, the injection
  counterpart of `getFallbackLocale`, same shape as `setDefaultLocale`.
- `createMemoryI18n`: holds the declared fallback and consults it per KEY after
  the requested locale, the way `FileI18nAdapter.t()` does. Undeclared keeps
  today's chain byte for byte.
- `AppPlugin.loadTranslations`: threads it through the same optional-capability
  probe, guarded on the app having declared something.

`getFallbackLocale()` stays deliberately absent from the memory fallback --
deriving one from `defaultLocale` would settle the contract question #14882
leaves open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/core, @objectstack/runtime, @objectstack/spec, touching 4 documentable anchor(s).

4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/email-templates.mdx (via II18nService (symbol, a top-level interface))
  • content/docs/kernel/services-checklist.mdx (via II18nService (symbol, a top-level interface), createMemoryI18n (symbol, a top-level function), setFallbackLocale (symbol, a method of interface II18nService))
  • content/docs/protocol/kernel/i18n-standard.mdx (via II18nService (symbol, a top-level interface), loadTranslations (symbol, a method of class AppPlugin))
  • content/docs/protocol/kernel/index.mdx (via II18nService (symbol, a top-level interface))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via II18nService (symbol, a top-level interface))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 140 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 9b459b791ea9c4d83c8563816b2eb9270359917epackageMentionDocs.

Which tree this was computed on

This run read content/docs from b18d8a2f1f515b45aac8ddbcd668fbd5dff9097d — the merge of head 22112c95410a4ce7bda0ae02d334a71cbc42aadd into base 9b459b791ea9c4d83c8563816b2eb9270359917e, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b18d8a2f1f515b45aac8ddbcd668fbd5dff9097d && git checkout b18d8a2f1f515b45aac8ddbcd668fbd5dff9097d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9b459b791ea9c4d83c8563816b2eb9270359917e 22112c95410a4ce7bda0ae02d334a71cbc42aadd && git checkout -B drift-repro 9b459b791ea9c4d83c8563816b2eb9270359917e && git merge --no-ff 22112c95410a4ce7bda0ae02d334a71cbc42aadd

node scripts/docs-audit/affected-docs.mjs --json 9b459b791ea9c4d83c8563816b2eb9270359917e

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 9b459b791ea9c4d83c8563816b2eb9270359917e → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Sep 6, 2026
`services-checklist.mdx` is the only page in `content/docs` that enumerates
what `AppPlugin.loadTranslations` injects, and the list was already missing
`setSupportedLocales` (#7679) before this branch. An enumeration presented as
complete is a claim, not silence, so it is corrected here rather than left one
item further behind by `setFallbackLocale`.

Also states the property all three share -- optional on `II18nService`, applied
through the same capability probe, guarded on the app having declared the value
-- since that is what makes the list safe to extend.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
…ents, and the injection order

Two factual corrections to prose landed earlier on this branch, both verified
against the source rather than restated:

- `FileI18nAdapter` was described as implementing "no setter for either" its
  default and fallback locales. It implements `setDefaultLocale` and
  `setSupportedLocales`; only `setFallbackLocale` is absent, because its
  `fallbackLocale` is `readonly` and fixed at construction. Omitting a setter is
  per-value, not per-provider, which is exactly why the capability probe is
  per-method.
- The `services-checklist` enumeration of what `AppPlugin.loadTranslations`
  injects is presented as an ordered list, so it now matches the code order:
  `setDefaultLocale` -> `setFallbackLocale` -> `setSupportedLocales` ->
  `loadTranslations`.

Also drops "and the dev plugin" from the new prose: `packages/cli`'s
`serve.ts:3125` is the only non-test site that constructs `I18nServicePlugin`
with `fallbackLocale || defaultLocale || 'en'`, and `os dev` reaches it by
spawning `serve` as a child. The pre-existing `getFallbackLocale` docblock
(#14882) keeps its own wording; it is not this card's to restate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
…ack-locale

Brings the branch onto a current base so the derived gate family and the local
gate union are measured against the tree CI will actually evaluate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
…leI18nAdapter's fallback locale

The previous commit narrowed "`os serve` and the dev plugin" to `os serve`
alone, on an incomplete grep that searched only `packages/runtime` and
`packages/cli`. `DevPlugin` does construct `I18nServicePlugin` --
`packages/plugins/plugin-dev/src/dev-plugin.ts:575`, from options built in
`dev-i18n.ts:247`, which collapses `fallbackLocale || defaultLocale || 'en'`
exactly as `packages/cli/src/commands/serve.ts:3125` does. Both boot paths, one
collapse; the prose now says so.

The `FileI18nAdapter` setter facts from the previous commit stand and are
unchanged: it implements `setDefaultLocale` and `setSupportedLocales`, its
`fallbackLocale` is `readonly` and constructor-only, and only
`setFallbackLocale` is absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ

Copy link
Copy Markdown
Contributor

Contract review (clause ②) — PASS — PR #16088 at head 22112c95 (Fixes #15694)

Director seat, summon #15, session_01TezFG8ZMrNH6n5VTNpPpdH (os-zhuang), 2026-09-06T02:50Z, batch review under the maintainer's 「按批次执行完所有的契约复审」. Tier fuse: get_session this session reads session_context.model = last_served_model = CONTRACT_REVIEW_TIER. Readings from the PR diff (7 files), card #15694 + triage 5549101360 + the dispatching seat's ruling 5555560813; dev report 5556284237 and PM record 5556304938 read afterwards as cross-check.

Implemented-by: session_01ARYe3yQTQCUFm5qPYNgKaJ os-dev rounds (branch claude/issue-15694-memory-i18n-fallback-locale; recovery round included)
Reviewed-by: session_01TezFG8ZMrNH6n5VTNpPpdH

Clause ② standing — yes, correctly re-derived (the seat's dispatch prediction was no)

Limb 1: II18nService in packages/spec/src/contracts/i18n-service.ts gains the optional member setFallbackLocale?(locale): void; createMemoryI18n's return type gains setFallbackLocale. Additive widening on two published carriers (@objectstack/spec via the ./contracts subpath, @objectstack/core), measured on every published .d.ts rather than the root barrel — the right instrument, and the reason check:api-surface staying green is not a no (it records export names, not members). Limb 2: no — the setter is optional, the injection is guarded on the app having declared a value, and a stack declaring none is pinned identical.

① Derived judgments

# claim reading verdict
1 i18n.fallbackLocale is authorable (TranslationConfigSchema) and honoured by FileI18nAdapter but inert on the in-memory fallback Card, triage and PR agree with firing controls; the declared-≠-enforced shape. The engine seat's ruling (learn the declaration, not "acceptable divergence") is within its lane and is the protocol-baseline direction: a declared key gets one answer whichever provider serves. correct
2 Per-key second leg after the requested locale, guarded on fallbackLocale set and !== locale; the pre-existing whole-bundle ?? mergedLocale(defaultLocale) leg unchanged Diff read: if (value === undefined && fallbackLocale && fallbackLocale !== locale); same shape as FileI18nAdapter.t(). correct
3 AppPlugin.loadTranslations threads it through the same optional-capability probe as setDefaultLocale, guarded on i18nConfig?.fallbackLocale Diff read; multi-AppPlugin reasoning (an app declaring nothing must not clear another's fallback) holds. correct
4 The interface member is necessary (probe compiles only because the member is declared — 2 × TS2551 without it) Measured; the as any alternative is the shape this file does not use. correct
5 getFallbackLocale() deliberately NOT added to the memory fallback (#14882's open default-locale question) Verified absent from the diff and pinned by a test. Correct fence. correct
6 file-i18n-adapter.ts untouched (contrast surface) Not in the file list. correct
7 Docs: services-checklist.mdx enumeration corrected (it was already one setter behind since #7679); eight other pages read and judged silent-not-false Accepted; content/docs/releases/** untouched. accepted

② semver

@objectstack/spec / core / runtime minor — additive public widening on the first two; runtime's own .d.ts is byte-identical but it is in the fixed group, so the level is moot in effect. Correct.

③ Boundary flags

Evidence and landing

Checks on 22112c95: 35 success / 2 skipped / 0 red; check-governed-merges --test on the 7 paths: 0 hits — ordinary queue landing. Clearing, same stroke: needs:contract-review off #15694 and PR #16088 with provenance; then check-clause2-carriers --pair 16088 ⇒ ready + auto-merge SQUASH from this seat.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants